﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace ScrumSim
{
    public class Level
    {
        private Game1 mGame;
        private LinkedList<GameObject>  mPlatforms;
        private LinkedList<Enemy>       mEnemies;
        private GameObject              mLastAddedPlatform;
        private Rectangle               mViewWindow;

        private Random mRand = new Random();

        private Texture2D mPlatformTexture;
        private Texture2D mEnemyIdleTexture;
        private Texture2D mEnemyMoveTexture;

        private double mScrollSpeed;
        private const double mMaxScrollSpeed = 5.0;
        private const double mAcceleration = 0.25;
        private double mDeltaX;

        public LinkedList<GameObject> plateforms { get { return mPlatforms; } }
        public LinkedList<Enemy> enemies { get { return mEnemies; } }

        public void setY(int pY) { mViewWindow.Y = pY; }
        public int getY() { return mViewWindow.Y; }

        public Level(Game1 pGame)
        {
            mGame = pGame;
            mPlatformTexture = mGame.Content.Load<Texture2D>("grassPlatform");
            mEnemyIdleTexture = mGame.Content.Load<Texture2D>("Enemy");
            mEnemyMoveTexture = mGame.Content.Load<Texture2D>("Enemy");
            mLastAddedPlatform = null;
        }

        public void Init()
        {
            mScrollSpeed = 0;
            mViewWindow = new Rectangle(0, 0,
                                        mGame.GraphicsDevice.PresentationParameters.BackBufferWidth,
                                        mGame.GraphicsDevice.PresentationParameters.BackBufferHeight);

            mPlatforms = new LinkedList<GameObject>();
            mEnemies = new LinkedList<Enemy>();
            GameObject lNewPlatform = new GameObject(mGame, mPlatformTexture);
            mPlatforms.AddLast(lNewPlatform);
            lNewPlatform.position = new Vector2(300.0f, 100.0f + (float)mRand.NextDouble() * (300));
            mLastAddedPlatform = lNewPlatform;

            foreach (GameObject lPlatform in mPlatforms)
                mGame.Components.Add(lPlatform);
        }

        public void Update(GameTime gameTime)
        {
            this.mScrollSpeed += (mAcceleration * gameTime.ElapsedGameTime.TotalSeconds);
            if (this.mScrollSpeed > mMaxScrollSpeed)
                this.mScrollSpeed = mMaxScrollSpeed;
            this.mDeltaX += this.mScrollSpeed;
            this.mViewWindow.X = (int)mDeltaX;

            this.RemoveOldPlatforms();
            this.RemoveOldEnemies();
            this.AddNewPlatforms();
        }

        private void RemoveOldPlatforms()
        {
            List<GameObject> lGarbage = new List<GameObject>(this.mPlatforms.Count);
            foreach (GameObject lPlatform in this.mPlatforms)
            {
                Rectangle lAbsoluteBBox =
                    new Rectangle(lPlatform.BBox.X + (int)lPlatform.position.X,
                                  lPlatform.BBox.Y + (int)lPlatform.position.Y,
                                  lPlatform.BBox.Width,
                                  lPlatform.BBox.Height);
                if (!mViewWindow.Intersects(lAbsoluteBBox) && (lAbsoluteBBox.X < mViewWindow.X))
                {
                    lGarbage.Add(lPlatform);
                    this.mGame.Components.Remove(lPlatform);
                }
            }
            for (int i = 0; i < lGarbage.Count; i++)
                this.mPlatforms.Remove(lGarbage[i]);
        }

        private void RemoveOldEnemies()
        {
            List<Enemy> lGarbage = new List<Enemy>(this.mEnemies.Count);
            foreach (Enemy lEnemy in this.mEnemies)
            {
                Rectangle lAbsoluteBBox =
                    new Rectangle(lEnemy.BBox.X + (int)lEnemy.position.X,
                                  lEnemy.BBox.Y + (int)lEnemy.position.Y,
                                  lEnemy.BBox.Width,
                                  lEnemy.BBox.Height);
                if (!mViewWindow.Intersects(lAbsoluteBBox) && (lAbsoluteBBox.X < mViewWindow.X))
                {
                    lGarbage.Add(lEnemy);
                    this.mGame.Components.Remove(lEnemy);
                }
            }
            for (int i = 0; i < lGarbage.Count; i++)
                this.mEnemies.Remove(lGarbage[i]);
        }

        private void AddNewPlatforms()
        {
            while (mLastAddedPlatform.position.X < (mViewWindow.X + mViewWindow.Width + mLastAddedPlatform.BBox.Width))
            {
                GameObject lNewPlatform = new GameObject(mGame, mPlatformTexture);

                float lNewDeltaY = (float)(0.5 - mRand.NextDouble()) * 10.0f * lNewPlatform.BBox.Height;

                if ((mLastAddedPlatform.position.Y + lNewDeltaY < 0)
                 || (mLastAddedPlatform.position.Y + lNewDeltaY - lNewPlatform.BBox.Height > mViewWindow.Height))
                    lNewDeltaY = -lNewDeltaY;

                lNewPlatform.position = 
                    new Vector2(
                        mLastAddedPlatform.position.X + 2 * mLastAddedPlatform.BBox.Width,
                        mLastAddedPlatform.position.Y + lNewDeltaY);

                mLastAddedPlatform = lNewPlatform;
                mPlatforms.AddLast(lNewPlatform);
                mGame.Components.Add(lNewPlatform);

                if (mRand.Next(2) != 0)
                {
                    Enemy lNewEnemy = new Enemy(mGame, mEnemyIdleTexture, mEnemyMoveTexture, lNewPlatform, mRand.Next(2) != 0);
                    lNewEnemy.position =
                        new Vector2(
                            lNewPlatform.position.X + mRand.Next(lNewPlatform.BBox.Width - lNewEnemy.BBox.Width),
                            lNewPlatform.position.Y - lNewEnemy.BBox.Height);
                    mEnemies.AddLast(lNewEnemy);
                    mGame.Components.Add(lNewEnemy);
                }
            }
        }

        public float GetDX()
        {
            return (float) this.mDeltaX;
        }
    }
}
